/*
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package au.com.domlogic.pvc.core.node;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.UserDataHandler;

/**
 *
 * A simple implementation of {@link Node} by Composition.
 *
 * @author Domenic Figliomeni
 *
 */
public class CompositeNode implements Node {

    private Node underlyingNode;

    public CompositeNode(Node aNode) {
        this.underlyingNode = aNode;
    }

    protected CompositeNode() {
    }

    protected Node getUnderlyingNode() {
        return underlyingNode;
    }

    protected void setUnderlyingNode(Node aNode) {
        this.underlyingNode = aNode;
    }

    /**
     * At the time of writing this framework, the Xerces implementation which I
     * was using first cast a passed node to an internal implementation.  The
     * maintainers of the Xerces project insist that this behaviour is in 
     * keeping with the spec so this method is used to safeguard against the
     * resulting class cast exception.
     * @param node
     * @return The underlying node if node is a composite node, else returns the
     * input node.
     */
    public static Node getRealNode(Node node) {
        Node realNode;
        if (node instanceof CompositeNode) {
            //Recurse through this method until we get to the underlying implementation
            realNode = getRealNode(((CompositeNode) node).getUnderlyingNode());
        } else {
            realNode = node;
        }
        return realNode;
    }

    /**
     * @see Node#appendChild(org.w3c.dom.Node)
     */
    @Override
    public Node appendChild(Node newChild) {
        return this.underlyingNode.appendChild(getRealNode(newChild));
    }

    /**
     *
     * @see Node#cloneNode(boolean)
     */
    @Override
    public Node cloneNode(boolean deep) {
        return this.underlyingNode.cloneNode(deep);
    }

    /**
     *
     * @see Node#compareDocumentPosition(org.w3c.dom.Node)
     */
    @Override
    public short compareDocumentPosition(Node other) {
        return this.underlyingNode.compareDocumentPosition(getRealNode(other));
    }

    /**
     *
     * @see Node#getAttributes()
     */
    @Override
    public NamedNodeMap getAttributes() {
        return this.underlyingNode.getAttributes();
    }

    /**
     *
     * @see Node#getBaseURI()
     */
    @Override
    public String getBaseURI() {
        return this.underlyingNode.getBaseURI();
    }

    /**
     *
     * @see Node#getChildNodes()
     */
    @Override
    public NodeList getChildNodes() {
        return this.underlyingNode.getChildNodes();
    }

    /**
     *
     * @see Node#getFeature(java.lang.String, java.lang.String)
     */
    @Override
    public Object getFeature(String feature, String version) {
        return this.underlyingNode.getFeature(feature, version);
    }

    /**
     *
     * @see Node#getFirstChild()
     */
    @Override
    public Node getFirstChild() {
        return this.underlyingNode.getFirstChild();
    }

    /**
     *
     * @see Node#getLastChild()
     */
    @Override
    public Node getLastChild() {
        return this.underlyingNode.getLastChild();
    }

    /**
     *
     * @see Node#getLocalName()
     */
    @Override
    public String getLocalName() {
        return this.underlyingNode.getLocalName();
    }

    /**
     *
     * @see Node#getNamespaceURI()
     */
    @Override
    public String getNamespaceURI() {
        return this.underlyingNode.getNamespaceURI();
    }

    /**
     *
     * @see Node#getNextSibling()
     */
    @Override
    public Node getNextSibling() {
        return this.underlyingNode.getNextSibling();
    }

    /**
     *
     * @see Node#getNodeName()
     */
    @Override
    public String getNodeName() {
        return this.underlyingNode.getNodeName();
    }

    /**
     *
     * @see Node#getNodeType()
     */
    @Override
    public short getNodeType() {
        return this.underlyingNode.getNodeType();
    }

    /**
     *
     * @see Node#getNodeValue()
     */
    @Override
    public String getNodeValue() {
        return this.underlyingNode.getNodeValue();
    }

    /**
     *
     * @see Node#getOwnerDocument()
     */
    @Override
    public Document getOwnerDocument() {
        return this.underlyingNode.getOwnerDocument();
    }

    /**
     *
     * @see Node#getParentNode()
     */
    @Override
    public Node getParentNode() {
        return this.underlyingNode.getParentNode();
    }

    /**
     *
     * @see Node#getPrefix()
     */
    @Override
    public String getPrefix() {
        return this.underlyingNode.getPrefix();
    }

    /**
     *
     * @see Node#getPreviousSibling()
     */
    @Override
    public Node getPreviousSibling() {
        return this.underlyingNode.getPreviousSibling();
    }

    /**
     *
     * @see Node#getTextContent()
     */
    @Override
    public String getTextContent() {
        return this.underlyingNode.getTextContent();
    }

    /**
     *
     * @see Node#getUserData(java.lang.String)
     */
    @Override
    public Object getUserData(String key) {
        return this.underlyingNode.getUserData(key);
    }

    /**
     *
     * @see Node#hasAttributes()
     */
    @Override
    public boolean hasAttributes() {
        return this.underlyingNode.hasAttributes();
    }

    /**
     *
     * @see Node#hasChildNodes()
     */
    @Override
    public boolean hasChildNodes() {
        return this.underlyingNode.hasChildNodes();
    }

    /**
     *
     * @see Node#insertBefore(org.w3c.dom.Node, org.w3c.dom.Node)
     */
    @Override
    public Node insertBefore(Node newChild, Node refChild) {
        return this.underlyingNode.insertBefore(getRealNode(newChild), getRealNode(refChild));
    }

    /**
     *
     * @see Node#isDefaultNamespace(java.lang.String)
     */
    @Override
    public boolean isDefaultNamespace(String namespaceURI) {
        return this.underlyingNode.isDefaultNamespace(namespaceURI);
    }

    /**
     *
     * @see Node#isEqualNode(org.w3c.dom.Node)
     */
    @Override
    public boolean isEqualNode(Node arg) {
        return this.underlyingNode.isEqualNode(getRealNode(arg));
    }

    /**
     *
     * @see Node#isSameNode(org.w3c.dom.Node)
     */
    @Override
    public boolean isSameNode(Node other) {
        return this.underlyingNode.isSameNode(getRealNode(other));
    }

    /**
     *
     * @see Node#isSupported(java.lang.String, java.lang.String)
     */
    @Override
    public boolean isSupported(String feature, String version) {
        return this.underlyingNode.isSupported(feature, version);
    }

    /**
     *
     * @see Node#lookupNamespaceURI(java.lang.String)
     */
    @Override
    public String lookupNamespaceURI(String prefix) {
        return this.underlyingNode.lookupNamespaceURI(prefix);
    }

    /**
     *
     * @see Node#lookupPrefix(java.lang.String)
     */
    @Override
    public String lookupPrefix(String namespaceURI) {
        return this.underlyingNode.lookupPrefix(namespaceURI);
    }

    /**
     *
     * @see Node#normalize()
     */
    @Override
    public void normalize() {
        this.underlyingNode.normalize();
    }

    /**
     *
     * @see Node#removeChild(org.w3c.dom.Node)
     */
    @Override
    public Node removeChild(Node oldChild) {
        return this.underlyingNode.removeChild(getRealNode(oldChild));
    }

    /**
     *
     * @see Node#replaceChild(org.w3c.dom.Node, org.w3c.dom.Node)
     */
    @Override
    public Node replaceChild(Node newChild, Node oldChild) {
        return this.underlyingNode.replaceChild(getRealNode(newChild), getRealNode(oldChild));
    }

    /**
     *
     * @see Node#setNodeValue(java.lang.String)
     */
    @Override
    public void setNodeValue(String nodeValue) {
        this.underlyingNode.setNodeValue(nodeValue);
    }

    /**
     *
     * @see Node#setPrefix(java.lang.String)
     */
    @Override
    public void setPrefix(String prefix) {
        this.underlyingNode.setPrefix(prefix);
    }

    /**
     * 
     * @see Node#setTextContent(java.lang.String)
     */
    @Override
    public void setTextContent(String textContent) {
        this.underlyingNode.setTextContent(textContent);
    }

    /**
     *
     * @see Node#setUserData(java.lang.String, java.lang.Object, org.w3c.dom.UserDataHandler)
     */
    @Override
    public Object setUserData(String arg0, Object arg1, UserDataHandler arg2) {
        return this.underlyingNode.setUserData(arg0, arg1, arg2);
    }
}
